﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.IO;





namespace TCP_Server_App
{
    public partial class FrmServer : Form
    {
        public FrmServer()
        {
            InitializeComponent();
        }

        //定义回调:解决跨线程访问问题
        private delegate void SetTextValueCallBack(string strValue);
        //定义接收客户端发送消息的回调
        private delegate void ReceiveMsgCallBack(string strReceive);
        //声明回调
        private SetTextValueCallBack setCallBack;
        //声明
        private ReceiveMsgCallBack receiveCallBack;
        //定义回调：给ComboBox控件添加元素
        private delegate void SetCmbCallBack(string strItem);
        //声明
        private SetCmbCallBack setCmbCallBack;
        //定义发送文件的回调
        private delegate void SendFileCallBack(byte[] bf);
        //声明
        private SendFileCallBack sendCallBack;

        //用于通信的Socket
        Socket socketSend;
        //用于监听的SOCKET
        Socket socketWatch;

        //将远程连接的客户端的IP地址和Socket存入集合中
        Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();

        //创建监听连接的线程
        Thread AcceptSocketThread;
        //接收客户端发送消息的线程
        Thread threadReceive;

        /// <summary>
        /// 开始监听
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Start_Click(object sender, EventArgs e)
        {
            Socket tempSocket = socketWatch; //后面socketWatch.Bind(point)执行异常处理发生时，需要恢复socketWatch的值
            try
            {
                //当点击开始监听的时候 在服务器端创建一个负责监听IP地址和端口号的Socket
                socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                //创建端口号
                IPEndPoint point;
                if (checkBox_WatchIPtype.Checked)
                {
                    point = new IPEndPoint(IPAddress.Any, Convert.ToInt32(this.txt_Port.Text.Trim()));
                }
                else
                {
                    IPAddress ip = IPAddress.Parse(this.txt_IP.Text.Trim()); //获取ip地址
                    point = new IPEndPoint(ip, Convert.ToInt32(this.txt_Port.Text.Trim()));
                }

                //绑定IP地址和端口号
                socketWatch.Bind(point);    //如果本处于监听状态，再次点击监听，会导致报异常--"通常每个套接字地址(协议/网络地址/端口)只允许使用一次" ,故后面的异常处理中，需要恢复socketWatch的值
                this.txt_Log.AppendText("\r\n监听成功" + " \r \n");
                this.txt_Log.AppendText("监听socket local ip: " + socketWatch.LocalEndPoint.ToString() + " \r \n");
                //this.txt_Log.AppendText("监听socket remote ip: " + socketWatch.RemoteEndPoint.ToString() + " \r \n"); //访问RemoteEndPoint将会异常
                //开始监听:设置最大可以同时连接多少个请求
                socketWatch.Listen(10);

                //实例化回调
                setCallBack = new SetTextValueCallBack(SetTextValue);
                receiveCallBack = new ReceiveMsgCallBack(ReceiveMsg);
                setCmbCallBack = new SetCmbCallBack(AddCmbItem);
                sendCallBack = new SendFileCallBack(SendFile);

                //创建线程
                Console.WriteLine("创建监听线程");
                AcceptSocketThread = new Thread(new ParameterizedThreadStart(StartListen));
                AcceptSocketThread.IsBackground = true;
                AcceptSocketThread.Start(socketWatch);
            }
            catch (Exception ex)
            {
                socketWatch = tempSocket;
                this.txt_Log.AppendText("开始监听异常： " + ex.Message + " \r \n");
                //MessageBox.Show("开始监听出现异常:" + ex.ToString());
            }
        }


        /// <summary>
        /// 监听线程 等待客户端的连接，并且创建与之通信用的Socket
        /// </summary>
        /// <param name="obj"></param>
        private void StartListen(object obj)
        {
            Socket socketWatch = obj as Socket;   //注释掉，不使用局部传递进来的SOCKET,而使用全局的socketWatch

            try
            {
                this.txt_Log.Invoke(receiveCallBack, string.Format("当前创建的监听线程id:{0}", Thread.CurrentThread.ManagedThreadId));
                while (true)
                {
                    //等待客户端的连接，并且创建一个用于通信的Socket
                    socketSend = socketWatch.Accept();  //(阻塞式)等待连接的到来
                    //获取远程主机的ip地址和端口号
                    string strIp = socketSend.RemoteEndPoint.ToString();
                    dicSocket.Add(strIp, socketSend);
                    this.cmb_Socket.Invoke(setCmbCallBack, strIp);
                    string strMsg = "\r\n远程主机：" + socketSend.RemoteEndPoint + "连接成功";
                    txt_Log.Invoke(setCallBack, strMsg);//使用回调
                    strMsg = "监听socket local ip: " + socketWatch.LocalEndPoint.ToString();
                    txt_Log.Invoke(setCallBack, strMsg);
                    strMsg = "会话socket local ip: " + socketSend.LocalEndPoint.ToString();
                    txt_Log.Invoke(setCallBack, strMsg);
                    strMsg = "会话socket remote ip: " + socketSend.RemoteEndPoint.ToString();
                    txt_Log.Invoke(setCallBack, strMsg);

                    //定义接收客户端消息的线程
                    threadReceive = new Thread(new ParameterizedThreadStart(Receive));
                    threadReceive.IsBackground = true;
                    threadReceive.Start(socketSend);
                }
            }
            catch (Exception ex)
            {
                txt_Log.Invoke(setCallBack, "监听线程异常：" + ex.Message);
                //MessageBox.Show("监听线程异常:" + ex.ToString());
            }
            finally
            {
                //socketWatch.Close(); //局部定义的socket貌似不用关闭，也行
                this.txt_Log.Invoke(receiveCallBack, string.Format("监听线程id:{0}退出", Thread.CurrentThread.ManagedThreadId));
            }
        }


        /// <summary>
        /// 服务器端不停的接收客户端发送的消息
        /// </summary>
        /// <param name="obj"></param>
        private void Receive(object obj)
        {
            Socket socketSend = obj as Socket;  //总是用创建会话连接时，传递进来的socket接收数据

            try
            {
                this.txt_Log.Invoke(receiveCallBack, string.Format("当前创建的会话线程id:{0}", Thread.CurrentThread.ManagedThreadId));
                //客户端连接成功后，服务器接收客户端发送的消息
                byte[] buffer = new byte[2048];
                while (true)
                {
                    Array.Clear(buffer, 0, buffer.Length);
                    //实际接收到的有效字节数
                    int count = socketSend.Receive(buffer);
                    if (count == 0)//count 表示客户端关闭，要退出循环
                    {
                        break;
                    }
                    else
                    {
                        Console.WriteLine("接收的数据：");    //打印原始数据 十六进制
                        int tempI = 0;
                        foreach (var item in buffer)
                        {
                            tempI++;
                            Console.Write("0x" + item.ToString("X2") + " ");
                            if (tempI >= count)
                                break;
                        }
                        Console.WriteLine();

                        string str = Encoding.Default.GetString(buffer, 0, count);
                        Console.WriteLine("接收到的STR："+str);
                        txt_Log.Invoke(receiveCallBack, string.Format("会话线程id:{2},local ip:{3} 接收远程客户端:{0}发送的消息:{1}", socketSend.RemoteEndPoint, str, Thread.CurrentThread.ManagedThreadId, socketSend.LocalEndPoint));
                    }
                }
            }
            catch (Exception ex)
            {
                string info = string.Format("数据接收异常1： {0}", ex.Message);
                txt_Log.Invoke(receiveCallBack, info);
                Console.WriteLine(info);
            }
            finally
            {
                this.txt_Log.Invoke(receiveCallBack, string.Format("会话线程id:{0}退出", Thread.CurrentThread.ManagedThreadId));
            }
        }

        /// <summary>
        /// 回调委托需要执行的方法
        /// </summary>
        /// <param name="strValue"></param>
        private void SetTextValue(string strValue)
        {
            this.txt_Log.AppendText(strValue + "\r\n");
        }


        private void ReceiveMsg(string strMsg)
        {
            this.txt_Log.AppendText(strMsg + "\r\n");
        }

        private void AddCmbItem(string strItem)
        {
            this.cmb_Socket.Items.Add(strItem);
            //cmb_Socket.SelectedIndex = cmb_Socket.Items.Count-1;  //方法1
            cmb_Socket.SelectedItem = strItem;                      //方法2
        }

        private void SendFile(byte[] sendBuffer)
        {

            try
            {
                //if (this.cmb_Socket.SelectedItem == null && socketSend != null)
                //    socketSend.Send(sendBuffer, SocketFlags.None);
                //else
                //{
                //    string ip = this.cmb_Socket.SelectedItem.ToString();
                //    dicSocket[ip].Send(sendBuffer, SocketFlags.None);
                //}
                dicSocket[cmb_Socket.SelectedItem.ToString()].Send(sendBuffer, SocketFlags.None);

                txt_Log.Invoke(receiveCallBack, string.Format("-->文件指定发给远程客户端:{0}", cmb_Socket.SelectedItem));
            }
            catch (Exception ex)
            {
                MessageBox.Show("发送文件出错:" + ex.Message);
            }
        }


        /// <summary>
        /// 服务器给客户端发送消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Send_Click(object sender, EventArgs e)
        {
            try
            {
                string strMsg = this.txt_Msg.Text.Trim();
                byte[] buffer = Encoding.Default.GetBytes(strMsg);
                List<byte> list = new List<byte>();
                //list.Add(byte.Parse("0"));//字节值将会是0x00
                //list.Add(Encoding.Default.GetBytes("0")[0]);
                list.AddRange(Encoding.Default.GetBytes("0")); //"0"表示发送的是消息

                list.AddRange(buffer);

                Console.WriteLine("发出的数据：");
                foreach (var item in list.ToArray())
                {
                    Console.Write("0x"+item.ToString("X2") + " ");
                }
                Console.WriteLine();

                //将泛型集合转换为数组
                byte[] newBuffer = list.ToArray();
                //获得用户选择的IP地址
                if (this.cmb_Socket.SelectedItem == null && socketSend != null)
                {
                    socketSend.Send(newBuffer);
                    txt_Log.Invoke(receiveCallBack, string.Format("--》》消息指定发给远程客户端:{0}", socketSend.RemoteEndPoint));
                }
                else
                {
                    string ip = this.cmb_Socket.SelectedItem.ToString();
                    dicSocket[ip].Send(newBuffer);
                    txt_Log.Invoke(receiveCallBack, string.Format("-->消息指定发给远程客户端:{0}", ip));
                }                
            }
            catch (Exception ex)
            {
                MessageBox.Show("给客户端发送消息出错:" + ex.Message);
            }
            //socketSend.Send(buffer);
        }


        /// <summary>
        /// 选择要发送的文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Select_Click(object sender, EventArgs e)
        {
            OpenFileDialog dia = new OpenFileDialog();
            //设置初始目录
            dia.InitialDirectory = @"";
            dia.Title = "请选择要发送的文件";
            //过滤文件类型
            dia.Filter = "所有文件|*.*";
            dia.ShowDialog();
            //将选择的文件的全路径赋值给文本框
            this.txt_FilePath.Text = dia.FileName;
        }


        /// <summary>
        /// 发送文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_SendFile_Click(object sender, EventArgs e)
        {
            List<byte> list = new List<byte>();
            //获取要发送的文件的路径
            string strPath = this.txt_FilePath.Text.Trim();
            using (FileStream sw = new FileStream(strPath, FileMode.Open, FileAccess.Read))
            {
                byte[] buffer = new byte[2048];
                int r = sw.Read(buffer, 0, buffer.Length);
                //list.Add(byte.Parse("1"));
                list.AddRange(Encoding.Default.GetBytes("1")); //"1"表示发送的是文件
                list.AddRange(buffer);

                byte[] newBuffer = list.ToArray();
                //发送
                //dicSocket[cmb_Socket.SelectedItem.ToString()].Send(newBuffer, 0, r+1, SocketFlags.None);
                btn_SendFile.Invoke(sendCallBack, newBuffer);


            }
        }


        /// <summary>
        /// 停止监听
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_StopListen_Click(object sender, EventArgs e)
        {
            try
            {
                //终止线程
                //if (AcceptSocketThread !=null && AcceptSocketThread.IsAlive)
                //    AcceptSocketThread.Abort();
                //if (threadReceive != null && threadReceive.IsAlive)
                //    threadReceive.Abort();
                if (AcceptSocketThread != null)
                {
                    AcceptSocketThread.Abort();
                    AcceptSocketThread = null;
                }
                if (threadReceive != null)
                {
                    threadReceive.Abort();
                    threadReceive = null;
                }

                if (socketWatch != null)
                {
                    socketWatch.Close();
                    socketWatch = null;
                }
                if (socketSend != null)
                {
                    socketSend.Close();
                    socketSend = null;
                }
            }
            catch (Exception ex)
            {
                string info = string.Format("数据接收异常2： {0}", ex.Message);
                txt_Log.Invoke(receiveCallBack, info);
                Console.WriteLine(info);
            }

        }

        private void FrmServer_FormClosing(object sender, FormClosingEventArgs e)
        {
            btn_StopListen_Click(sender, e);
        }

        private void button_clear_Click(object sender, EventArgs e)
        {
            txt_Log.Text = "";
        }

        private void checkBox_WatchIPtype_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBox_WatchIPtype.Checked)
            { txt_IP.Enabled = false; }
            else
            { txt_IP.Enabled = true; }
        }

        private void FrmServer_Load(object sender, EventArgs e)
        {
            checkBox_WatchIPtype_CheckedChanged(sender, e);
        }
    }
}
